# -*- coding: utf-8 -*-
from django import forms
from skoolmaster.syllabus.models import UniversityProgram, SchoolYear, Subject
from skoolmaster.exams.models import Exam, ExamApplication, TooManyApplications, TooManyApplicationsThisYear, AlreadyApplied, RecentApplicationTooClose, ApplicationDateHasExpired,\
  MustPay
from skoolmaster.syllabus.forms import SelectSubjectForm
from skoolmaster.general.widgets import CustomDateWidget, CustomSplitDateTime, CustomTimeWidget, HorizontalFilterWidget
from skoolmaster.general.models import UserProfile
from skoolmaster.general.fields import LecturersField
from django.contrib.admin import widgets 
import datetime

class ExamManualApplicationForm(forms.Form):
  """
  A form for manually entering a student's exam application.
  """
  date = forms.DateField(label = u"Datum", widget = CustomDateWidget())
  seq_no = forms.IntegerField(
    label = u"Polaganje",
    min_value = 1,
    max_value = 6,
    error_messages = {
      'min_value' : u"Zaporedna št. polaganja mora biti najmanj %s!",
      'max_value' : u"Zaporedna št. polaganja je lahko največ %s!"
    }
  )
  
  def __init__(self, student, subject, *args, **kwargs):
    """
    Class constructor.
    """
    super(ExamManualApplicationForm, self).__init__(*args, **kwargs)
    
    # Save student and subject for later
    self.student, self.subject = student, subject
    
    # Populate examiner choices depending on the subject
    self.fields['examiners'] = LecturersField(
      label = u"Izpraševalci",
      queryset = subject.lecturers.all(),
      widget = HorizontalFilterWidget('examiners', title = u"izpraševalci")
    )
    self.fields['examiners'].top_label = True
    
    # Create grade fields depending on subject grading type
    if subject.has_grade_exam():
      self.fields['grade_exam'] = forms.IntegerField(
        label = u"Ocena izpita",
        min_value = 5,
        max_value = 10,
        error_messages = {
          'min_value' : u"Ocena izpita mora biti najmanj %s!",
          'max_value' : u"Ocena izpita je lahko največ %s!"
        }
      )
    
    if subject.has_grade_tut():
      self.fields['grade_tut'] = forms.IntegerField(
        label = u"Ocena vaj",
        min_value = 5,
        max_value = 10,
        error_messages = {
          'min_value' : u"Ocena vaj mora biti najmanj %s!",
          'max_value' : u"Ocena vaj je lahko največ %s!"
        }
      )
  
  def clean(self):
    """
    Additional validation handler.
    """
    seq_no = self.cleaned_data.get('seq_no')
    date = self.cleaned_data.get('date')
    grade_exam = self.cleaned_data.get('grade_exam')
    grade_tut = self.cleaned_data.get('grade_tut')
    
    # Check if both grades are pass or not
    if grade_exam and grade_tut:
      passed_exam = int(grade_exam) > 5
      passed_tut = int(grade_tut) > 5
      
      if passed_exam != passed_tut:
        raise forms.ValidationError(u"Obe oceni morata biti ali pozitivni ali negativni!")

    # Check that the date is not in the future
    if date and date > datetime.date.today():
      raise forms.ValidationError(u"Datum izpita ne more biti v prihodnosti!")
    
    # Check that this sequence number is not already taken
    if ExamApplication.objects.filter(
        student = self.student,
        exam__subject = self.subject,
        seq_no = seq_no
      ):
      raise forms.ValidationError(u"Za ta predmet že obstaja polaganje pod to zaporedno številko!")
    
    # Check that this subject doesn't already have grades for this student
    if ExamApplication.objects.filter(
        student = self.student,
        exam__subject = self.subject,
        passed = True
      ):
      raise forms.ValidationError(u"Študent je že opravil navedeni izpit!")
    
    # Check that we have a valid school year
    try:
      date = self.cleaned_data.get('date')
      if date:
        self.cleaned_data['school_year'] = SchoolYear.objects.get(start__lte = date, end__gte = date)
    except:
      raise forms.ValidationError(u"Navedeni datum ne pade v nobeno obstoječe študijsko leto!")
    
    return self.cleaned_data
  
  def save(self):
    """
    Saves this manual exam application form.
    """
    # Create a new Exam so we will be able to list this grade somewhere
    exam = Exam()
    exam.subject = self.subject
    exam.date = self.cleaned_data.get('date')
    exam.school_year = self.cleaned_data.get('school_year')
    exam.location = ''
    exam.locked = True
    exam.save()
    
    # Add selected examiners
    exam.examiners.add(*self.cleaned_data.get('examiners'))
    
    # Add an exam application
    app = ExamApplication()
    app.student = self.student.user
    app.exam = exam
    app.grade_exam = self.cleaned_data.get('grade_exam')
    app.grade_tut = self.cleaned_data.get('grade_tut')
    app.seq_no = self.cleaned_data.get('seq_no')
    app.save()

class ExamApplicationFormAdmin(forms.Form):
  """ 
  Form for managing exam applications by the admin
  """

  stud_num = forms.CharField(max_length = 8, label = u'Vpisna številka:')
  
  def __init__(self, stud_num=None, exam=None, *args, **kwargs):
    """
    Class constructor.
    """
    super(ExamApplicationFormAdmin, self).__init__(*args, **kwargs)
    
    if stud_num:
      self.fields['stud_num'].initial = stud_num
      student = UserProfile.objects.get(student_number = stud_num)
      
      # Get a list of subjects that the user has already passed (for later exclusion)
      passed_subjects = Subject.objects.filter(
        exams__examapplication__passed = True,
        exams__examapplication__student = student
      ).distinct()
      
      self.fields['exam'] = forms.ModelChoiceField(
        queryset = Exam.objects.filter(
          subject__students = student,
          locked = False
        ).exclude(  # exclude those exams that student already signed up for        
          applications = student 
        ).exclude(
          subject__in=passed_subjects  # exclude exams from subjects that student already passed  
        ).order_by('date'),
        empty_label = None,
        label = "Izpit"      
      )
    
    if exam:    
      self.fields['exam'].initial = getattr(exam, 'pk', None)

class ExamApplicationForm(forms.Form):
  """ 
  Form for managing exam applications 
  """
  def __init__(self, user, *args, **kwargs):
    """
    Class constructor.
    """
    super(ExamApplicationForm, self).__init__(*args, **kwargs)
    self.__user = user
    
    # Get a list of subjects that the user has already passed (for later exclusion)
    passed_subjects = Subject.objects.filter(
      exams__examapplication__passed = True,
      exams__examapplication__student = user
    ).distinct()
    
    self.fields['exam'] = forms.ModelChoiceField(
      queryset = Exam.objects.filter(
        subject__students = user.get_profile(),
        school_year = SchoolYear.objects.latest(),
        locked = False
      ).exclude(  # exclude those exams that student already signed up for        
        applications = user        
      ).exclude(  # exclude those exams that are less than 3 days away
        date__lte = datetime.datetime.now() + datetime.timedelta(days = 3)
      ).exclude(
        subject__in = passed_subjects  # exclude exams from subjects that student already passed
      ).order_by('date'),
      empty_label = None,
      label = "Izpit"      
    )
  
  def save(self):
    """
    Creates a new exam application.
    """
    exam = self.cleaned_data['exam']
    app = ExamApplication(exam = exam, student = self.__user)
    app.must_pay = self.cleaned_data.get('must_pay', False)
    app.save(force_insert = True)
  
  def clean(self):
    """
    Additional validation handler.
    """
    exam = self.cleaned_data.get('exam')
    if exam is None:
      return
    
    # Validate exam application count
    try:
      exam.check_can_take(self.__user)
    except TooManyApplications:
      raise forms.ValidationError(u"Celotno število polaganj tega izpita je preseženo!")
    except TooManyApplicationsThisYear:
      raise forms.ValidationError(u"Število polaganj tega izpita za to leto je preseženo!")
    except AlreadyApplied:
      raise forms.ValidationError(u"Na izpit za ta predmet ste že prijavljeni!")
    except RecentApplicationTooClose:
      raise forms.ValidationError(u"Ne morete se prijaviti na izpit prej kot 14 dni od zadnjega polaganja!")
    except ApplicationDateHasExpired:
      raise forms.ValidationError(u"Rok za prijavo je potekel!")
    except MustPay:
      self.cleaned_data['must_pay'] = True
    
    return self.cleaned_data

class SelectExamForm(SelectSubjectForm):
  """
  Class for producing forms for exam selection.
  
  Based on the given information the form displays
  the appropriate fields.
  """
  def __init__(self, prog_id=None, year_id=None, module_id=None, subject_id=None, *args, **kwargs):
    super(SelectExamForm, self).__init__(prog_id, year_id, module_id)
    
    # Provide available exams based on the selected subject
    if subject_id:
      # In case of selection changes
      try:
        selected_subject = self.selected_module.subjects.get(id=subject_id)
      except:
        return
      self.fields['subject'].initial = getattr(selected_subject, 'id', None)
      self.fields['exam'] = forms.ModelChoiceField(
        Exam.objects.filter(subject=subject_id).order_by("-date"),
        label = u'Izpitni rok'
      )
    else:
      return

class InsertExamForm(forms.Form):
  """
  Class for producing forms for exam insertion.
  """
  def __init__(self, subject_id=None, *args, **kwargs):
    super(InsertExamForm, self).__init__(*args, **kwargs)
    
    
    if subject_id:
      
      try:
        selected_subject = Subject.objects.get(id=subject_id)
      except:
        return
    
      lecturers = selected_subject.current_lecturers.all()
      self.fields['school_year'] = forms.ModelChoiceField(SchoolYear.objects.all(),label='Šolsko leto', empty_label=None)
      self.fields['datetime_exam'] = forms.DateTimeField(widget = CustomSplitDateTime(), label='Izpitni rok')
      self.fields['place'] = forms.CharField(max_length=20,label='Prostor')
      self.fields['max_points'] = forms.IntegerField(label='Največje možno število točk') 
      self.fields['examiners'] = forms.MultipleChoiceField( 
                                                           label = u"Izpraševalci",
                                                           choices = [(lecturer.pk, str(lecturer.get_profile())) for lecturer in lecturers],
                                                           widget = HorizontalFilterWidget('examiners', title = u"izvajalci" )
                                                           )
      self.fields['examiners'].top_label = True
    
 
    else:
      return
  
  
class EditExamForm(forms.Form):
  """
  Class for producing forms for editing an exam.
  """
  def __init__(self, subject_id=None, exam_id=None, *args, **kwargs):
    super(EditExamForm, self).__init__(*args, **kwargs)
    
    
    if subject_id and exam_id:
      
      try:
        selected_subject = Subject.objects.get(id=subject_id)
        exam = Exam.objects.get(id=exam_id)
      except:
        return
    
      lecturers = selected_subject.current_lecturers.all()
      examiners = exam.examiners.all()
      
      self.fields['school_year'] = forms.ModelChoiceField(SchoolYear.objects.all(),label='Šolsko leto', empty_label=None)
      self.fields['school_year'].initial = exam.school_year
      self.fields['datetime_exam'] = forms.DateTimeField(widget = CustomSplitDateTime(), label='Izpitni rok')
      self.fields['datetime_exam'].initial = exam.date 
      self.fields['place'] = forms.CharField(max_length=20,label='Prostor')
      self.fields['place'].initial = exam.location
      self.fields['max_points'] = forms.IntegerField(label='Največje možno število točk') 
      self.fields['max_points'].initial = exam.max_points
      self.fields['examiners'] = forms.MultipleChoiceField( 
                                                           label = u"Izpraševalci",
                                                           choices = [(lecturer.pk, str(lecturer.get_profile())) for lecturer in lecturers],
                                                           initial = [(examiner.pk) for examiner in examiners],
                                                           widget = HorizontalFilterWidget('examiners', title = u"izvajalci" )
                                                           )
      self.fields['examiners'].top_label = True
      

    else:
      return
  



